<script>
import fecha from 'element-ui/src/utils/date'
import { range as rangeArr, getFirstDayOfMonth, getPrevMonthLastDays, getMonthDays, getI18nSettings, validateRangeInOneMonth } from 'element-ui/src/utils/date-util'

export default {
  props: {
    selectedDay: String, // formated date yyyy-MM-dd
    range: {
      type: Array,
      validator(val) {
        if (!(val && val.length)) return true
        const [start, end] = val
        return validateRangeInOneMonth(start, end)
      }
    },
    date: Date,
    hideHeader: Boolean,
    firstDayOfWeek: Number
  },

  inject: ['elCalendar'],

  data() {
    return {
      WEEK_DAYS: getI18nSettings().dayNames
    }
  },

  methods: {
    toNestedArr(days) {
      return rangeArr(days.length / 7).map((_, index) => {
        const start = index * 7
        return days.slice(start, start + 7)
      })
    },

    getFormateDate(day, type) {
      if (!day || ['prev', 'current', 'next'].indexOf(type) === -1) {
        throw new Error('invalid day or type')
      }
      let prefix = this.curMonthDatePrefix
      if (type === 'prev') {
        prefix = this.prevMonthDatePrefix
      } else if (type === 'next') {
        prefix = this.nextMonthDatePrefix
      }
      day = `00${day}`.slice(-2)
      return `${prefix}-${day}`
    },
    getCellClass({ text, type }) {
      const classes = [type]
      if (type === 'current') {
        const date = this.getFormateDate(text, type)
        if (date === this.selectedDay) {
          classes.push('is-selected')
        }
        if (date === this.formatedToday) {
          classes.push('is-today')
        }
      }
      return classes
    },

    pickDay({ text, type }) {
      const date = this.getFormateDate(text, type)
      this.$emit('pick', date)
    },

    cellRenderProxy({ text, type }) {
      const render = this.elCalendar.$scopedSlots.dateCell
      if (!render) return '<span>{ text }</span>'

      const day = this.getFormateDate(text, type)
      const date = new Date(day)
      const data = {
        isSelected: this.selectedDay === day,
        type: `${type}-month`,
        day
      }
      return render({ date, data })
    }
  },

  computed: {
    prevMonthDatePrefix() {
      const temp = new Date(this.date.getTime())
      temp.setDate(0)
      return fecha.format(temp, 'yyyy-MM')
    },

    curMonthDatePrefix() {
      return fecha.format(this.date, 'yyyy-MM')
    },

    nextMonthDatePrefix() {
      const temp = new Date(this.date.getFullYear(), this.date.getMonth() + 1, 1)
      return fecha.format(temp, 'yyyy-MM')
    },

    formatedToday() {
      return this.elCalendar.formatedToday
    },

    isInRange() {
      return this.range && this.range.length
    },

    rows() {
      let days = []
      // if range exists, should render days in range.
      if (this.isInRange) {
        const [start, end] = this.range
        const currentMonthRange = rangeArr(end.getDate() - start.getDate() + 1).map((_, index) => ({
          text: start.getDate() + index,
          type: 'current'
        }))
        let remaining = currentMonthRange.length % 7
        remaining = remaining === 0 ? 0 : 7 - remaining
        const nextMonthRange = rangeArr(remaining).map((_, index) => ({
          text: index + 1,
          type: 'next'
        }))
        days = currentMonthRange.concat(nextMonthRange)
      } else {
        const date = this.date
        let firstDay = getFirstDayOfMonth(date)
        firstDay = firstDay === 0 ? 7 : firstDay
        const firstDayOfWeek = typeof this.firstDayOfWeek === 'number' ? this.firstDayOfWeek : 1
        const prevMonthDays = getPrevMonthLastDays(date, firstDay - firstDayOfWeek).map(day => ({
          text: day,
          type: 'prev'
        }))
        const currentMonthDays = getMonthDays(date).map(day => ({
          text: day,
          type: 'current'
        }))
        days = [...prevMonthDays, ...currentMonthDays]
        const nextMonthDays = rangeArr(42 - days.length).map((_, index) => ({
          text: index + 1,
          type: 'next'
        }))
        days = days.concat(nextMonthDays)
      }
      return this.toNestedArr(days)
    },

    weekDays() {
      const start = this.firstDayOfWeek
      const { WEEK_DAYS } = this

      if (typeof start !== 'number' || start === 0) {
        return WEEK_DAYS.slice()
      } else {
        return WEEK_DAYS.slice(start).concat(WEEK_DAYS.slice(0, start))
      }
    }
  },

  render() {
    const thead = this.hideHeader ? null : (<thead>
      {
        this.weekDays.map(day => <th key={day}>{ day }</th>)
      }
    </thead>)
    return (
      <table
        class={{
          'el-calendar-table': true,
          'is-range': this.isInRange
        }}
        cellspacing="0"
        cellpadding="0">
        {
          thead
        }
        <tbody>
          {
            this.rows.map((row, index) => <tr
              class={{
                'el-calendar-table__row': true,
                'el-calendar-table__row--hide-border': index === 0 && this.hideHeader
              }}
              key={index}>
              {
                row.map((cell, key) => <td key={key}
                  class={ this.getCellClass(cell) }
                  onClick={this.pickDay.bind(this, cell)}>
                  <div class="el-calendar-day">
                    {
                      this.cellRenderProxy(cell)
                    }
                  </div>
                </td>)
              }
            </tr>)
          }
        </tbody>
      </table>)
  }
}
</script>
